#include "nodeevents.h"
#include "yaml-cpp/node/node.h"
#include "yaml-cpp/node/impl.h"
#include "yaml-cpp/eventhandler.h"
#include "yaml-cpp/mark.h"

namespace YAML
{
	void NodeEvents::AliasManager::RegisterReference(const detail::node& node)
	{
		m_anchorByIdentity.insert(std::make_pair(node.ref(), _CreateNewAnchor()));
	}

	anchor_t NodeEvents::AliasManager::LookupAnchor(const detail::node& node) const
	{
		AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(node.ref());
		if (it == m_anchorByIdentity.end())
			return 0;
		return it->second;
	}

	NodeEvents::NodeEvents(const Node& node) : m_pMemory(node.m_pMemory), m_root(*node.m_pNode)
	{
		Setup(m_root);
	}

	void NodeEvents::Setup(const detail::node& node)
	{
		int& refCount = m_refCount[node.ref()];
		refCount++;
		if (refCount > 1)
			return;

		if (node.type() == NodeType::Sequence) {
			for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it)
				Setup(**it);
		}
		else if (node.type() == NodeType::Map) {
			for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it) {
				Setup(*it->first);
				Setup(*it->second);
			}
		}
	}

	void NodeEvents::Emit(EventHandler& handler)
	{
		AliasManager am;

		handler.OnDocumentStart(Mark());
		Emit(m_root, handler, am);
		handler.OnDocumentEnd();
	}

	void NodeEvents::Emit(const detail::node& node, EventHandler& handler, AliasManager& am) const
	{
		anchor_t anchor = NullAnchor;
		if (IsAliased(node)) {
			anchor = am.LookupAnchor(node);
			if (anchor) {
				handler.OnAlias(Mark(), anchor);
				return;
			}

			am.RegisterReference(node);
			anchor = am.LookupAnchor(node);
		}

		switch (node.type()) {
		case NodeType::Undefined:
			break;
		case NodeType::Null:
			handler.OnNull(Mark(), anchor);
			break;
		case NodeType::Scalar:
			handler.OnScalar(Mark(), node.tag(), anchor, node.scalar());
			break;
		case NodeType::Sequence:
			handler.OnSequenceStart(Mark(), node.tag(), anchor);
			for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it)
				Emit(**it, handler, am);
			handler.OnSequenceEnd();
			break;
		case NodeType::Map:
			handler.OnMapStart(Mark(), node.tag(), anchor);
			for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it) {
				Emit(*it->first, handler, am);
				Emit(*it->second, handler, am);
			}
			handler.OnMapEnd();
			break;
		}
	}

	bool NodeEvents::IsAliased(const detail::node& node) const
	{
		RefCount::const_iterator it = m_refCount.find(node.ref());
		return it != m_refCount.end() && it->second > 1;
	}
}